/*
 *     Prionace - A file navigator to manage your files
 *
 *     Copyright (C) 2012 Francivan Bezerra
 *
 *     This file is part of Prionace.
 *
 *     Prionace is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     Prionace is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with Prionace.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.fba.prionace.file;

import java.awt.AlphaComposite;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.MediaTracker;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.ConcurrentLinkedQueue;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;

import org.apache.tika.Tika;
import org.imgscalr.Scalr;
import org.imgscalr.Scalr.Method;

public class IconFactory implements Runnable {

	private static IconFactory instance;

	private ConcurrentLinkedQueue<Thumbnail> thumbnails;

	private static volatile boolean threadRunning;

	private IconFactory() {
		thumbnails = new ConcurrentLinkedQueue<Thumbnail>();
	}

	private static synchronized IconFactory createInstance() {
		if (instance == null) {
			instance = new IconFactory();
		}

		return instance;
	}

	public static IconFactory getInstance() {
		if (instance == null) {
			instance = createInstance();
			threadRunning = false;
		}

		return instance;
	}

	public ImageIcon createIcon(File file, Dimension size) throws IOException {
		Tika tika = new Tika();
		String mimeType;
		ImageIcon createdIcon = null;

		try {
			mimeType = tika.detect(file).replace('/', '-');
		} catch (Exception e) {
			mimeType = "unknown";
		}

		if (mimeType.startsWith("image")) {
			String md5name = getMD5Name(file);
			File thumbnail = new File(System.getProperty("user.home")
					.concat("/.prionace/thumbnails/").concat(md5name));

			if (!thumbnail.exists()) {
				createThumbnail(file, thumbnail, size);
			}

			createdIcon = new ImageIcon(thumbnail.getAbsolutePath());

			if (createdIcon.getImageLoadStatus() != MediaTracker.COMPLETE) {
				createdIcon.getImage().flush();
				createdIcon = null;
			}
		}

		if (createdIcon == null) {
			File imageIcon;

			if (file.isDirectory()) {
				imageIcon = new File(System.getProperty("user.home").concat(
						"/.prionace/mimetypes/folder.png"));
			} else {
				imageIcon = new File(System.getProperty("user.home")
						.concat("/.prionace/mimetypes/").concat(mimeType)
						.concat(".png"));

				if (!imageIcon.exists()) {
					try {
						new File(getClass().getResource(
								"/mimetypes/" + mimeType + ".png").getFile());
					} catch (NullPointerException e) {
						imageIcon = new File(System.getProperty("user.home")
								.concat("/.prionace/mimetypes/file.png"));
					}
				}
			}

			if (imageIcon.exists()) {
				createdIcon = new ImageIcon(imageIcon.getAbsolutePath());
			} else {
				String resource;

				if (file.isDirectory()) {
					resource = "/mimetypes/folder.png";
				} else {
					resource = "/mimetypes/" + mimeType + ".png";

					try {
						new File(resource.getClass().getResource(resource)
								.getFile());
					} catch (NullPointerException e) {
						resource = "/mimetypes/file.png";
					}
				}

				createdIcon = new ImageIcon(resource.getClass().getResource(
						resource));
			}
		}

		if (file.isHidden()) {
			BufferedImage image = new BufferedImage(createdIcon.getIconWidth(),
					createdIcon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
			Graphics2D g2d = image.createGraphics();
			g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC,
					0.5f));
			g2d.drawImage(createdIcon.getImage(), 0, 0, null);
			g2d.dispose();
			createdIcon.getImage().flush();
			createdIcon = new ImageIcon(image);
		}

		return createdIcon;
	}

	private String getMD5Name(File file) {
		String md5 = null;
		MessageDigest digest;

		try {
			digest = MessageDigest.getInstance("MD5");
			digest.update(file.getAbsolutePath().getBytes(), 0, file
					.getAbsolutePath().length());
			md5 = new BigInteger(1, digest.digest()).toString(16);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}

		return md5;
	}

	private void createThumbnail(File imageFile, File thumbnailFile,
			Dimension size) throws IOException {
		thumbnails.add(new Thumbnail(imageFile, thumbnailFile, size));

		if (!threadRunning) {
			threadRunning = true;
			Thread thread = new Thread(this);
			thread.setDaemon(true);
			thread.start();
		}
	}

	public void run() {
		while (!thumbnails.isEmpty()) {
			try {
				thumbnails.remove().createThumbnail();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		threadRunning = false;
	}
}

class Thumbnail {

	private File origin;

	private File destination;

	private Dimension size;

	public Thumbnail(File origin, File destination, Dimension size) {
		this.origin = origin;
		this.destination = destination;
		this.size = size;
	}

	public void createThumbnail() throws IOException {
		BufferedImage image = ImageIO.read(origin);

		if (image == null
				|| origin.getParent().equals(
						System.getProperty("user.home")
								.concat("/.prionace/thumbnails")
								.replace('/', File.separatorChar))) {
			return;
		}

		if (!destination.exists()) {

			float ratio = Math.min((float) size.width / image.getWidth(),
					(float) size.height / image.getWidth());

			ratio = Math.min(ratio, 1f);

			BufferedImage thumbnail = Scalr.resize(image, Method.QUALITY,
					(int) (image.getWidth() * ratio),
					(int) (image.getHeight() * ratio), Scalr.OP_ANTIALIAS);

			if (!destination.getParentFile().exists()) {
				destination.getParentFile().mkdirs();
			}

			image.flush();
			ImageIO.write(thumbnail, "png", destination);
		}
	}
}
